/**
 * 2.函数和方法的隐式 Deref 强制转换(这一点非常重要)
 * 
 * a.将实现了 Deref trait 的类型的引用转换为另一种类型的引用
 * 能转换为另外一种引用
 * 
 * b.Deref 强制转换是 Rust 在函数或方法传参上的一种便利操作，并且只能作用于实现了 Deref trait 的类型。
 * 当这种特定类型的引用作为实参传递给和形参类型不同的函数或方法时将自动进行。这时会有一系列的 deref 方法被调用，把我们提供的类型转换成了参数所需的类型
 * 即自动转换,是一种便利
 * Deref 强制转换的加入使得 Rust 程序员编写函数和方法调用时无需增加过多显式使用 & 和 * 的引用和解引用。这个功能也使得我们可以编写更多同时作用于引用或智能指针的代码
 * rust也不得不屈服了，否则代码实在过于艰涩难懂
 * 
 * c.符号*操作用来解引用。意思就是如果一个变量T原来是引用类型，那么在T前面添加*，那么获得的就是T所指向值，并同时获得
 * 所有权。
 */
use std::ops::Deref;
use std::any::type_name;


struct MyBox<T>(T);
impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}
//需要注意的是，自定义这个，仅仅是简单地返回一个引用
//这个具体的实现和Box的是不一样的。Box返回的是T，而不是&T
impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        //从返回类型可以看出，所谓的Deref,其实是提供引用
        &(self.0)
    }
}

 
fn hello_ref_string(name: &str) {
    println!("Hello, {name}!");
}

fn hello_pure_string(name:String){
    println!("Hello, {name}!");    
}


//打印变量类型
fn print_type_of<T>(_: &T) {
    println!("The type is: {}", type_name::<T>());
}

fn main() {
    let m = MyBox::new(String::from("Rust"));
    hello_ref_string(&m); //标准库中提供了 String 上的 Deref 实现，其会返回字符串 slice，这可以在 Deref 的 API 文档中看到。
               //Rust 再次调用 deref 将 &String 变为 &str，这就符合 hello_ref_string 函数的定义了

    //hello_ref_string(&(*m)[..]);  

    //1.0 Box的隐式转换
    let china=String::from("中国");
    let me=Box::new(china);
    let tt=*me;   // tt已经夺取了china的所有权
    hello_pure_string(tt);
    //println!("{}",&china);  错误的语句 ，因为china的所有权已经被me夺取了。

    let eng=String::from("英国佬");
    let eng_box=Box::new(eng);
    hello_ref_string(&eng_box);  // 隐式转换，把 &Box转为 &String。 通过这种引用可以省掉 * 符号


    //2.0 隐式转换示例 -- 真正的隐式转换：引用转换为另一种类型的引用
    let you=MyBox::new(String::from("德国"));
    //实现情况之一
    let you_str=&*you;
    hello_ref_string(you_str);

    //实现情况之二
    let him=MyBox::new(String::from("日本"));
    hello_ref_string(&him);  //这里会产生隐式转换，rust会自动调用deref方法。效果等于 &*him。把MyBox的引用转为&String（&str)
    hello_ref_string(&*him);  

    let box_egg=Box::new(String::from("鸡蛋"));
    let good_egg=*box_egg.clone()+"-- 西瓜";
    let bad_egg=&box_egg;
    println!("{}",box_egg);
    println!("{}",good_egg);
    println!("{}",bad_egg);
}